package com.ant.gprnter.flutter_gprinter

import android.content.Intent
import android.widget.Toast
import androidx.annotation.NonNull
import com.alibaba.fastjson.JSON
import com.ant.gprnter.bean.ECommand
import com.ant.gprnter.bean.TextCommand
import com.gprinter.bean.PrinterDevices
import com.gprinter.command.EscCommand
import com.gprinter.utils.CallbackListener
import com.gprinter.utils.Command
import com.gprinter.utils.ConnMethod
import com.gprinter.utils.Menu58Utils
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result

/** FlutterGprinterPlugin */
class FlutterGprinterPlugin : FlutterPlugin, MethodCallHandler, ActivityAware {
    /// The MethodChannel that will the communication between Flutter and native Android
    ///
    /// This local reference serves to register the plugin with the Flutter Engine and unregister it
    /// when the Flutter Engine is detached from the Activity

    private val USB_REQUEST_CODE = 10000
    private val BT_REQUEST_CODE = 10001
    private val WIFI_REQUEST_CODE = 10002
    private lateinit var channel: MethodChannel
    private lateinit var binding: ActivityPluginBinding

    private val printerListener = object : CallbackListener {
        override fun onConnecting() {
        }

        override fun onCheckCommand() {
        }

        override fun onSuccess(printerDevices: PrinterDevices?) {
            var connectInfo = when (printerDevices?.connMethod) {
                ConnMethod.BLUETOOTH, ConnMethod.BLE_BLUETOOTH -> printerDevices.macAddress
                ConnMethod.USB -> printerDevices.usbDevice.deviceName
                ConnMethod.WIFI -> printerDevices.ip
                else -> null
            }
            channel.invokeMethod(
                "connect",
                mapOf("code" to 1, "address" to connectInfo)
            )
            UIUtils.toast(binding.activity, R.string.conn_success);
        }

        override fun onReceive(data: ByteArray?) {
        }

        override fun onFailure() {
            channel.invokeMethod(
                "connect",
                mapOf("code" to 2)
            )
        }

        override fun onDisconnect() {
            channel.invokeMethod(
                "connect",
                mapOf("code" to 4)
            )
        }

    }

    override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
        channel = MethodChannel(flutterPluginBinding.binaryMessenger, "flutter_gprinter")
        channel.setMethodCallHandler(this)
    }

    override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) {
        if (call.method == "getPlatformVersion") {
            result.success("Android ${android.os.Build.VERSION.RELEASE}")
        } else if (call.method == "navUsbDevices") {
            val intent = Intent(binding.activity, UsbDeviceActivity::class.java)
            binding.activity.startActivityForResult(intent, USB_REQUEST_CODE)
        } else if (call.method == "navBTDevices") {
            val intent = Intent(binding.activity, SelectBluetoothActivity::class.java)
            binding.activity.startActivityForResult(intent, BT_REQUEST_CODE)
        } else if (call.method == "navWifiDevices") {
            val intent = Intent(binding.activity, WifiDeviceActivity::class.java)
            binding.activity.startActivityForResult(intent, WIFI_REQUEST_CODE)
        } else if (call.method == "autoConnect") {
            autoConnect()
        } else if (call.method == "write") {
            if (call.arguments is Map<*, *>) {
                val args = call.arguments as Map<*, *>
                val bytes = args["bytes"] as List<Int>
                Printer.portManager?.writeDataImmediately(bytes.toTypedArray().toByteArray());
            }
        } else if (call.method == "connectUSB") {
            val pathName = call.argument<String>("pathName")
            pathName?.let {
                connectUSB(pathName)
            }
        } else if (call.method == "connectNet") {
            val ipAddress = call.argument<String>("ipAddress")
            ipAddress?.let {
                connectWifi(ipAddress)
            }
        } else if (call.method == "connectBt") {
            val macAddress = call.argument<String>("macAddress")
            macAddress?.let {
                connectBT(macAddress)
            }
        } else if (call.method == "disconnect") {
            Printer.close()
        } else {
            result.notImplemented()
        }
    }

    private fun Array<out Int>.toByteArray(): ByteArray {
        return ByteArray(size) { index -> this[index].toByte() }
    }

    private fun autoConnect() {
        val macAddress =
            SharedPreferencesUtil.getInstantiation(binding.activity).getString(null, "mac_address")
        val usbName =
            SharedPreferencesUtil.getInstantiation(binding.activity).getString(null, "usb_name")
        val ip = SharedPreferencesUtil.getInstantiation(binding.activity).getString(null, "ip")

        if (macAddress != null) {
            val blueTooth = PrinterDevices.Build()
                .setContext(binding.activity)
                .setConnMethod(ConnMethod.BLUETOOTH)
                .setMacAddress(macAddress)
                .setCommand(Command.ESC)
                .setCallbackListener(printerListener)
                .build()
            Printer.connect(blueTooth)

            Printer.close()
            return
        }

        if (usbName != null) {
            val usbDevice = Utils.getUsbDeviceFromName(binding.activity, usbName)
            val usb = PrinterDevices.Build()
                .setContext(binding.activity)
                .setConnMethod(ConnMethod.USB)
                .setUsbDevice(usbDevice)
                .setCommand(Command.ESC)
                .setCallbackListener(printerListener)
                .build()
            Printer.connect(usb)
        }

        if (ip != null) {
            val wifi = PrinterDevices.Build()
                .setContext(binding.activity)
                .setConnMethod(ConnMethod.WIFI)
                .setIp(ip)
                .setPort(9100) //打印唯一端口9100
                .setCommand(Command.ESC)
                .setCallbackListener(printerListener)
                .build()
            Printer.connect(wifi)
        }
    }

    override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) {
        channel.setMethodCallHandler(null)
    }

    override fun onAttachedToActivity(binding: ActivityPluginBinding) {
        this.binding = binding
        binding.addActivityResultListener { requestCode, resultCode, data ->
            if (requestCode == USB_REQUEST_CODE) {
                data?.let {
                    connectUsbDevice(it)
                }
            } else if (requestCode == BT_REQUEST_CODE) {
                data?.let {
                    connectBTDevice(it)
                }
            } else if (requestCode == WIFI_REQUEST_CODE) {
                data?.let {
                    connectWifiDevice(it)
                }
            }
            true
        }
    }


    override fun onDetachedFromActivityForConfigChanges() {
    }

    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {
    }

    override fun onDetachedFromActivity() {
    }

    private fun toEscCommand(commands: MutableList<Map<String, Any>>): EscCommand {
        var esc = EscCommand()
        commands.forEach { that ->
            val type = that["type"] as Int

            when (type) {
                0 -> {
                    val eCommand = JSON.parseObject(JSON.toJSONString(that), ECommand::class.java)
                    convert(esc, eCommand)
                }

                1 -> {
                    val textCommand =
                        JSON.parseObject(JSON.toJSONString(that), TextCommand::class.java)
                    textCommand.characterSize?.let {
                        esc.addSetCharcterSize(
                            EscCommand.WIDTH_ZOOM.values()[it.width],
                            EscCommand.HEIGHT_ZOOM.values()[it.height]
                        )
                    }
                    textCommand.underlineMode?.let {
                        esc.addTurnUnderlineModeOnOrOff(EscCommand.UNDERLINE_MODE.values()[it])
                    }
                    textCommand.kanjiUnderLine?.let {
                        esc.addSetKanjiUnderLine(EscCommand.UNDERLINE_MODE.values()[it])
                    }
                    textCommand.selectJustification?.let {
                        esc.addSelectJustification(EscCommand.JUSTIFICATION.values()[it])
                    }
                    if (textCommand.contents.size == 1) {
                        esc.addText(textCommand.contents[0])
                    } else if (textCommand.contents.size == 2) {
                        esc.addText(
                            Menu58Utils.printTwoData(
                                textCommand.contents[0],
                                textCommand.contents[1]
                            )
                        )
                    } else if (textCommand.contents.size == 3) {
                        esc.addText(
                            Menu58Utils.printThreeData(
                                textCommand.contents[0],
                                textCommand.contents[1],
                                textCommand.contents[2]
                            )
                        )
                    } else if (textCommand.contents.size == 4) {
                        esc.addText(
                            Menu58Utils.printFourData(
                                textCommand.contents[0],
                                textCommand.contents[1],
                                textCommand.contents[2],
                                textCommand.contents[3]
                            )
                        )
                    }
                    convert(esc, textCommand)
                }

                2 -> {

                }

                3 -> {

                }
            }
        }
        return esc;
    }

    private fun convert(esc: EscCommand, eCommand: ECommand) {
        if (eCommand.printAndLineFeed == true) {
            esc.addPrintAndLineFeed()
        }
        eCommand.printAndFeedLines?.let {
            esc.addPrintAndFeedLines(it.toByte())
        }
        if (eCommand.cutPaper == true) {
            esc.addCutPaper()
        }
        if (eCommand.initializePrinter == true) {
            esc.addInitializePrinter()
        }
    }

    private fun connectBTDevice(data: Intent) {
        val mac = data.getStringExtra(BlueToothDeviceActivity.EXTRA_DEVICE_ADDRESS)
        SharedPreferencesUtil.getInstantiation(binding.activity).putString(mac, "mac_address")
        val blueTooth = PrinterDevices.Build()
            .setContext(binding.activity)
            .setConnMethod(ConnMethod.BLUETOOTH)
            .setMacAddress(mac)
            .setCommand(Command.ESC)
            .setCallbackListener(printerListener)
            .build()
        Printer.connect(blueTooth)
    }

    private fun connectBT(macAddress: String) {
        val blueTooth = PrinterDevices.Build()
            .setContext(binding.activity)
            .setConnMethod(ConnMethod.BLUETOOTH)
            .setMacAddress(macAddress)
            .setCommand(Command.ESC)
            .setCallbackListener(printerListener)
            .build()
        Printer.connect(blueTooth)
    }


    private fun connectWifiDevice(data: Intent) {
        val ip = data.getStringExtra(WifiDeviceActivity.IP)
        SharedPreferencesUtil.getInstantiation(binding.activity).putString(ip, "ip")
        val wifi = PrinterDevices.Build()
            .setContext(binding.activity)
            .setConnMethod(ConnMethod.WIFI)
            .setIp(ip)
            .setPort(9100) //打印唯一端口9100
            .setCommand(Command.ESC)
            .setCallbackListener(printerListener)
            .build()
        Printer.connect(wifi)
    }

    private fun connectWifi(ip: String) {
        val wifi = PrinterDevices.Build()
            .setContext(binding.activity)
            .setConnMethod(ConnMethod.WIFI)
            .setIp(ip)
            .setPort(9100) //打印唯一端口9100
            .setCommand(Command.ESC)
            .setCallbackListener(printerListener)
            .build()
        Printer.connect(wifi)
    }

    private fun connectUsbDevice(data: Intent) {
        val name: String? = data.getStringExtra(UsbDeviceActivity.USB_NAME)
        if (name.isNullOrEmpty()) return
        SharedPreferencesUtil.getInstantiation(binding.activity).putString(name, "usb_name")
        val usbDevice = Utils.getUsbDeviceFromName(binding.activity, name)
        val usb = PrinterDevices.Build()
            .setContext(binding.activity)
            .setConnMethod(ConnMethod.USB)
            .setUsbDevice(usbDevice)
            .setCommand(Command.ESC)
            .setCallbackListener(printerListener)
            .build()
        Printer.connect(usb)
    }

    private fun connectUSB(usb: String) {
        val usbDevice = Utils.getUsbDeviceFromName(binding.activity, usb)
        val device = PrinterDevices.Build()
            .setContext(binding.activity)
            .setConnMethod(ConnMethod.USB)
            .setUsbDevice(usbDevice)
            .setCommand(Command.ESC)
            .setCallbackListener(printerListener)
            .build()
        Printer.connect(device)
    }
}
